﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Xml.Linq;
using GameEngine.Libs;

namespace GameEngine.Model.Data
{
    public class GameVO<IdType> : IGameVO<IdType>
        where IdType : IComparable<IdType>
    {
        
        //Properties:

        public IGameVOId Id { get; set; }
        public string Name { get; set; }
        public Point TileSize { get; set; }
        public float MaxHeight { get; set; }
        public float MinHeight { get; set; }
        public float TileHeightUnit { get; set; }

        public IDictionary<int, ILevelVOId> LevelOrder { get; set; }
        public int CurrentLevel { get; set; }

        //Containers

        public List<IViewStateVO> ViewStates { get; set; }
        public List<IThingTypeVO<IdType>> ThingTypes { get; set; }
        public List<IViewVO<IdType>> Views { get; set; }
        public List<IResourceVO> Resources { get; set; }
        public List<ILevelVO<IdType>> Levels { get; set; }

        public GameVO(IGameVOId id)
        {
            Id = id;
            Name = "New Game";
            CurrentLevel = 0;
            TileSize = new Point() { X = 10, Y = 10 };
            MaxHeight = 1;
            MinHeight = 0;
            TileHeightUnit = 0.1f;
            Initialize();
            ViewStates.Add(IViewVOConstants.DefaultState);
        }

        public GameVO()
        {
            Initialize();
        }

        void Initialize() {
            ThingTypes = new List<IThingTypeVO<IdType>>();
            Levels = new List<ILevelVO<IdType>>();
            Views = new List<IViewVO<IdType>>();
            Resources = new List<IResourceVO>();
            LevelOrder = new Dictionary<int, ILevelVOId>();
            ViewStates = new List<IViewStateVO>();
        }

        public virtual string Serialize()
        {
            //return Serializer.Serialize(this);
            XDocument doc = new XDocument();
            XElement mainNode = new XElement("GameVO");
            mainNode.SetAttributeValue("Class", GetType());

            {
                XElement node = new XElement("Id", new XAttribute("Class", Id.GetType()));
                node.Add(Id.Serialize());
                mainNode.Add(node);
            }
            {
                XElement node = new XElement("Name") { Value = Name };
                mainNode.Add(node);
            }
            {
                XElement node = new XElement("TileSize",
                    new XAttribute("X", TileSize.X),
                    new XAttribute("Y", TileSize.Y));
                mainNode.Add(node);
            }
            {
                XElement node = new XElement("MaxHeight") { Value = MaxHeight.ToString() };
                mainNode.Add(node);
            }
            {
                XElement node = new XElement("MinHeight") { Value = MinHeight.ToString() };
                mainNode.Add(node);
            }
            {
                XElement node = new XElement("TileHeightUnit") { Value = TileHeightUnit.ToString() };
                mainNode.Add(node);
            }

            {
                XElement node = new XElement("ViewStates");
                foreach (var row in ViewStates)
                {
                    XElement rowXml = new XElement("ViewState", new XAttribute("Class", row.GetType()));
                    rowXml.Add(row.Value);
                    node.Add(rowXml);
                }
                mainNode.Add(node);
            }

            {
                XElement node = new XElement("ThingTypes");
                foreach (var row in ThingTypes)
                {
                    XElement rowXml = new XElement("ThingType", new XAttribute("Class", row.GetType()));
                    rowXml.Add(row.Serialize());
                    node.Add(rowXml);
                }
                mainNode.Add(node);
            }

            {
                XElement node = new XElement("CurrentLevel") { Value = CurrentLevel.ToString() };
                    mainNode.Add(node);
            }
            {
                XElement node = new XElement("Levels");
                foreach (var row in Levels)
                {
                    XElement rowXml = new XElement("Level", new XAttribute("Class", row.GetType()));
                    rowXml.Add(row.Serialize());
                    node.Add(rowXml);
                }
                mainNode.Add(node);
            }
            {
                XElement node = new XElement("LevelOrder");
                foreach (var row in LevelOrder)
                {
                    XElement rowXml = new XElement("LevelOrder",
                        new XAttribute("Class", row.Value.GetType()),
                        new XAttribute("key", row.Key.ToString()));
                    rowXml.Add(row.Value.Serialize());
                    node.Add(rowXml);
                }
                mainNode.Add(node);
            }
            {
                XElement node = new XElement("Views");
                foreach (var row in Views)
                {
                    XElement rowXml = new XElement("View", new XAttribute("Class", row.GetType()));
                    rowXml.Add(row.Serialize());
                    node.Add(rowXml);
                }
                mainNode.Add(node);
            }

            return mainNode.ToString();
        }

        public virtual void DeSerialize(string serializedString) {

            var mainNode = XElement.Parse(serializedString);
            foreach (XElement node in mainNode.Nodes())
            {
                switch (node.Name.LocalName)
                {
                    case "Name":
                        Name = node.Value;
                        break;
                    case "Id":
                        {
                            var className = node.Attribute("Class").Value;
                            var classType = Type.GetType(className);
                            var instance = Activator.CreateInstance(classType) as IGameVOId;
                            instance.DeSerialize(node.Value);
                            Id = instance;
                        }
                        break;
                    case "TileSize":
                        TileSize = new Point(
                            double.Parse(node.Attribute("X").Value),
                            double.Parse(node.Attribute("Y").Value));
                        break;
                    case "MinHeight":
                        MinHeight = float.Parse(node.Value);
                        break;
                    case "MaxHeight":
                        MaxHeight = float.Parse(node.Value);
                        break;
                    case "TileHeightUnit":
                        TileHeightUnit = float.Parse(node.Value);
                        break;
                    case "ViewStates":
                        { 
                        foreach (var row in node.Elements())
                        {
                            var className = row.Attribute("Class").Value;
                            var classType = Type.GetType(className);
                            var instance = Activator.CreateInstance(classType, node.Value) as IViewStateVO;
                            ViewStates.Add(instance);
                        }
                        }
                        break;
                    case "ThingTypes":
                        {
                            foreach (var row in node.Elements())
                            {
                                var className = row.Attribute("Class").Value;
                                var instance = createInstance(className) as IThingTypeVO<IdType>;
                                instance.DeSerialize(row.Value);
                                ThingTypes.Add(instance);
                            }
                        }
                        break;
                    case "CurrentLevel":
                        CurrentLevel = int.Parse(node.Value);
                        break;
                    case "Levels":
                        {
                            foreach (var row in node.Elements())
                            {
                                var className = row.Attribute("Class").Value;
                                var classType = Type.GetType(className);
                                var instance = Activator.CreateInstance(classType) as ILevelVO<IdType>;
                                instance.DeSerialize(row.Value);
                                Levels.Add(instance);
                            }
                        }
                        break;
                    case "LevelOrder":
                        {
                            foreach (var row in node.Elements())
                            {
                                int key = int.Parse(row.Attribute("key").Value);
                                var className = row.Attribute("Class").Value;
                                var classType = Type.GetType(className);
                                var instance = Activator.CreateInstance(classType, node.Value) as ILevelVOId;
                                LevelOrder.Add(key, instance);
                            }
                        }
                        break;
                    case "Views":
                        {
                            foreach (var row in node.Elements())
                            {
                                var className = row.Attribute("Class").Value;
                                var instance = createInstance(className, row.Value) as IViewVO<IdType>;
                                Views.Add(instance);
                            }
                        }
                        break;
                }
            }
        }

        /// <summary>
        /// When class are in other assembly override this method in that assembly
        /// </summary>
        /// <param name="className"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public virtual object createInstance(String className, params object[] args) {
            var classType = Type.GetType(className);
            return Activator.CreateInstance(classType,args);
        }
    }

    public class SimpleId<type> : ISimpleId<type>
    {
        public type Value { get; set; }

        public SimpleId(type value) {
            Value = value;
        }

        public SimpleId()
        {
        }

        public virtual XElement createSerializeObject()
        {
            XElement mainNode = new XElement("MainNode");
            //mainNode.SetAttributeValue("Class", GetType());
            {
                XElement node = new XElement("Value");
                node.Add(new XCData(Serializer.Serialize(Value)));
                mainNode.Add(node);
            }
            return mainNode;
        }

        public virtual void DeSerialize(string serializedString)
        {
            XElement mainNode = XElement.Parse(serializedString);
            Value = (type)Serializer.DeSerialize(typeof(type), mainNode.Element("Value").Value);
        }

        public string Serialize()
        {
            return createSerializeObject().ToString();
        }

        public override int GetHashCode()
        {
            return Value.GetHashCode();
        }


        public override bool Equals(object obj)
        {
            return Equals(obj as SimpleId<type>);
        }        

        public bool Equals(SimpleId<type> obj)
        {
            return obj != null && obj.Value.Equals(this.Value);
        }

    }

    public class Id<type> : SimpleId<type>, IId<type>
    {
        public string Name { get; set; }

        public Id() { }

        public Id(type id) : base(id) { }

        public void DeSerialize(string serializedString)
        {
            base.DeSerialize(serializedString);
            XElement mainNode = XElement.Parse(serializedString);
            Name = mainNode.Element("Name").Value;
        }

        public override XElement createSerializeObject()
        {
            XElement mainNode = base.createSerializeObject();
            //mainNode.SetAttributeValue("Class", GetType());
            {
                XElement node = new XElement("Name");
                node.Add(Name);
                mainNode.Add(node);
            }
            return mainNode;
        }

        public override string ToString()
        {
            return Name;
        }
    }

    public class GameVOId : SimpleId<string>, IGameVOId{
        public GameVOId(string id) : base(id) { }
        public GameVOId() { }
    }
}